/**
 * 提现模型。包含提现申请与通过记录。
 */
const constants = require('../../utils/constants');
const models = require('../../utils/models');

const WxPayment = require('../vendor/tencent/payment');
const wxPayment = new WxPayment();

module.exports = class Withdraw extends think.Model {
    get tableName() {
        return 'user_withdraw';
    }

    /**
     * 增加一条提现申请记录。增加前对余额进行校验。提现规则：
     * 1 如果用户提现金额 + 提现手续费 <= 余额，通过提现，从余额中扣除手续费
     * 2 如果用户提现金额 + 提现手续费 > 余额，通过提现，但从提现金额中扣除手续费
     *
     * 如果用户有渠道，需要给用户渠道分成。
     */
    async createOne(userId, openid, appid, amount) {
        try {
            const settingsModel = think.model(models.Settings);
            const settings = await settingsModel.getServerSideSettings();

            // 每天提现次数限制
            const count = await this.where(`userId = ${userId} and to_days(requestTime) = to_days(now())`).count();
            if (count >= settings['withdrawMaxTimesPerDay']) {
                return {error: true, message: '今日提现次数达到上限，请明日再提现!'};
            }

            if (amount < 100) return {error: true, message: '根据微信规定，提现金额不能小于1元!'};
            if (amount > 2000000) return {error: true, message: '根据微信规定，单次提现最高2万!'};

            const balanceModel = think.model(models.UserBalance).db(this.db());
            const res = await balanceModel.getBalance(userId);

            if (!res.pass) return {error: true, message: '余额校验失败!'};
            if (res.balance < amount) return { error: true, message: '余额不足!' };

            const fee = Math.ceil(amount * settings['feeRatio']);
            if ((res.balance - amount) < fee) {
                // 余额扣除提现金额后，不足以支付手续费，则要判断是否还够1元，如果够就可以提现，否则不能提现
                if (res.balance - fee < 100) {
                    return { error: true, message: '扣除提现手续费之后，您的余额不足1元，无法满足微信提现要求!' };
                } else {
                    amount = res.balance - fee;
                }
            }

            const withdraw = {
                userId: userId,
                openid: openid,
                appid: appid,
                amount: amount
            };

            await this.startTrans();

            withdraw.id = await this.add(withdraw, {});                                         // 插入提现记录
            const updateRes = await balanceModel.updateBalance(userId, -(amount + fee),         // 减少余额，含手续费
                constants.BalanceChangeType.Withdraw);
            withdraw.balance = updateRes.balance;

            // 给渠道计算佣金，增加用户余额
            const userModel = think.model(models.User).db(this.db());
            const user = await userModel.field('channelId').where({id: userId}).find();
            if (user.channelId) {
                const channelModel = think.model(models.Channel).db(this.db());
                const channel = await channelModel.getOne(user.channelId);
                if (!think.isEmpty(channel)) {
                    const commision = Math.floor(channel.commisionRatio * fee);
                    const channelBalance = await balanceModel.getBalance(channel.userId);

                    // 余额校验通过，计算佣金
                    if (channelBalance.pass) {
                        await balanceModel.updateBalance(channel.userId, commision,
                            constants.BalanceChangeType.Commision
                        );
                    }
                }
            }

            await this.commit();
            return withdraw;
        } catch (err) {
            think.logger.error('提现失败: ' + err.message);

            return {error: true, message: '提现失败: ' + err.message};
        }
    }

    /**
     * 通过提现申请。由系统参数决定是否自动通过。为防止replay攻击，
     * 确认前，先检查是否有余额小于0的变更历史，如果有，则停止提现申请。
     *
     * 作为异步处理，不直接返回结果。
     *
     * PS：此方法由管理后台调用。
     */
    async confirmRequest(withdrawId) {
        const bhModel = think.model(models.UserBalanceHistory);
        const list = bhModel.where({after: ['EXP', ' < 0']}).select();
        if (list.length > 0) {
            think.logger.warn('出现金额为0的用户提现，可能是replay造成的!');
            return;
        }

        const withdraw = await this.where({id: withdrawId}).find();
        if (think.isEmpty(withdraw) || withdraw.status === constants.WithdrawStatus.Confirmed) return;

        const appModel = think.model(models.VendorTencentWechatApp);
        const merchantInfo = await appModel.getRelativeMerchant(withdraw.appid);

        const transferNo = withdraw.userId + 'U' + new Date().getTime();
        const transferRes = await wxPayment.trasferToUser(
            withdraw.openid,
            withdraw.appid,
            withdraw.amount,
            transferNo,
            '福袋送祝福提现',
            merchantInfo);

        if (transferRes.error) {
            // 转账失败
            await this.where({id: withdrawId}).update({
                status: constants.WithdrawStatus.Failed,
                notes: transferRes.message
            });
        } else {
            // 转账成功
            await this.where({id: withdrawId}).update({
                confirmTime: ['EXP', 'CURRENT_TIMESTAMP'],
                transferNo: transferNo,
                transferResult: JSON.stringify(transferRes.message),
                status: constants.WithdrawStatus.Confirmed
            });
        }
    }
};
